package cn.zhxu.data.fastjson2;

import cn.zhxu.data.Array;
import cn.zhxu.data.DataConvertor;
import cn.zhxu.data.Mapper;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.ContextValueFilter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

/**
 * @author Troy.Zhou @ 2017-03-20
 * @since v1.5.0
 * */
public class Fastjson2DataConvertor implements DataConvertor {

    // 用于传递格式
    public static final ThreadLocal<String> FORMAT_HOLDER = new ThreadLocal<>();

    private final ContextValueFilter valueFilter = (context, object, name, value) -> {
        String format = context.getFormat();
        if (format != null && !format.isEmpty()) {
            FORMAT_HOLDER.set(format);
        } else {
            FORMAT_HOLDER.remove();
        }
        return value;
    };

    public Fastjson2DataConvertor() {
        JSON.register(LocalTime.class, new TemporalWriter("HH:mm"));
        JSON.register(LocalDate.class, new TemporalWriter("yyyy-MM-dd"));
        JSON.configWriterDateFormat("yyyy-MM-dd HH:mm");    // 序列化
        JSON.configReaderDateFormat("yyyy-MM-dd HH:mm:ss"); // 反序列化
    }

    @Override
    public Mapper toMapper(InputStream in, Charset charset) {
        return toMapper(toString(in, charset));
    }

    @Override
    public Mapper toMapper(String in) {
        return new Fastjson2Mapper(JSON.parseObject(in));
    }

    @Override
    public Array toArray(InputStream in, Charset charset) {
        return toArray(toString(in, charset));
    }

    @Override
    public Array toArray(String in) {
        return new Fastjson2Array(JSON.parseArray(in));
    }

    @Override
    public byte[] serialize(Object object, Charset charset) {
        return serialize(object, false).getBytes(charset);
    }

    @Override
    public byte[] serialize(Object object, Charset charset, boolean pretty) {
        return serialize(object, pretty).getBytes(charset);
    }

    @Override
    public String serialize(Object object, boolean pretty) {
        if (object instanceof Mapper) {
            object = ((Mapper) object).toMap();
        }
        if (object instanceof Array) {
            object = ((Array) object).toList();
        }
        if (pretty) {
            return JSON.toJSONString(object, valueFilter, JSONWriter.Feature.PrettyFormat);
        }
        return JSON.toJSONString(object, valueFilter);
    }

    @Override
    public <T> T toBean(Type type, InputStream in, Charset charset) {
        return JSON.parseObject(in, charset, type);
    }

    @Override
    public <T> T toBean(Type type, String in) {
        return JSON.parseObject(in, type);
    }

    @Override
    public <T> List<T> toList(Class<T> type, InputStream in, Charset charset) {
        return toList(type, toString(in, charset));
    }

    @Override
    public <T> List<T> toList(Class<T> type, String in) {
        return JSON.parseArray(in, type);
    }

    protected String toString(InputStream in, Charset charset) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buff = new byte[512];
        try {
            int len;
            while ((len = in.read(buff)) > 0) {
                output.write(buff, 0, len);
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        try {
            return output.toString(charset.name());
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

}
